const HIDE_TIMEOUT = 0.2;
-const ICON_SIZE = 24;
const BUTTON_ICON_SIZE = 36;
const MAX_SOURCE_TITLE_WIDTH = 180;
// @banner: the banner text
// @params: optional additional params
//
-// Creates a notification. In banner mode, it will show
-// @source's icon, @title (in bold) and @banner, all on a single line
+// Creates a notification. In banner mode, it will show an
+// icon, @title (in bold) and @banner, all on a single line
// (with @banner ellipsized if necessary).
//
+// By default, the icon shown is created by calling
+// source.createNotificationIcon(). However, you can override this
+// by passing an 'icon' parameter in @params.
+//
// Additional notification details can be added, in which case the
// notification can be expanded by moving the pointer into it. In
// expanded mode, the banner text disappears, and there can be one or
update: function(title, banner, params) {
params = Params.parse(params, { bannerBody: this._bannerBody,
body: null,
+ icon: null,
clear: false });
if (this._icon)
this._bannerBody = params.bannerBody;
- this._icon = this.source.createIcon(ICON_SIZE);
+ this._icon = params.icon || this.source.createNotificationIcon();
this.actor.add(this._icon, { row: 0,
col: 0,
x_expand: false,
};
Signals.addSignalMethods(Notification.prototype);
-function Source(title, createIcon) {
- this._init(title, createIcon);
+function Source(title) {
+ this._init(title);
}
Source.prototype = {
- _init: function(title, createIcon) {
+ ICON_SIZE: 24,
+
+ _init: function(title) {
this.title = title;
- if (createIcon)
- this.createIcon = createIcon;
+ this._iconBin = new St.Bin({ width: this.ICON_SIZE,
+ height: this.ICON_SIZE });
+ },
+
+ // Called to create a new icon actor (of size this.ICON_SIZE).
+ // Must be overridden by the subclass if you do not pass icons
+ // explicitly to the Notification() constructor.
+ createNotificationIcon: function() {
+ throw new Error('no implementation of createNotificationIcon in ' + this);
},
- // This can be overridden by a subclass, or by the createIcon
- // parameter to _init()
- createIcon: function(size) {
- throw new Error('no implementation of createIcon in ' + this);
+ // Unlike createNotificationIcon, this always returns the same actor;
+ // there is only one summary icon actor for a Source.
+ getSummaryIcon: function() {
+ return this._iconBin;
},
notify: function(notification) {
destroy: function() {
this.emit('destroy');
+ },
+
+ //// Protected methods ////
+
+ // The subclass must call this at least once to set the summary icon.
+ _setSummaryIcon: function(icon) {
+ if (this._iconBin.child)
+ this._iconBin.child.destroy();
+ this._iconBin.child = icon;
}
};
Signals.addSignalMethods(Source.prototype);
this._sourceBox.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this._sourceBox.connect('allocate', Lang.bind(this, this._allocate));
- this._sourceIcon = source.createIcon(ICON_SIZE);
+ this._sourceIcon = source.getSummaryIcon();
this._sourceTitleBin = new St.Bin({ y_align: St.Align.MIDDLE, x_fill: true });
this._sourceTitle = new St.Label({ style_class: 'source-title',
text: source.title });
}
},
+ _iconForNotificationData: function(icon, hints, size) {
+ let textureCache = St.TextureCache.get_default();
+
+ if (icon) {
+ if (icon.substr(0, 7) == 'file://')
+ return textureCache.load_uri_async(icon, size, size);
+ else if (icon[0] == '/') {
+ let uri = GLib.filename_to_uri(icon, null);
+ return textureCache.load_uri_async(uri, size, size);
+ } else
+ return textureCache.load_icon_name(icon, size);
+ } else if (hints.icon_data) {
+ let [width, height, rowStride, hasAlpha,
+ bitsPerSample, nChannels, data] = hints.icon_data;
+ return textureCache.load_from_raw(data, data.length, hasAlpha,
+ width, height, rowStride, size);
+ } else {
+ let stockIcon;
+ switch (hints.urgency) {
+ case Urgency.LOW:
+ case Urgency.NORMAL:
+ stockIcon = 'gtk-dialog-info';
+ break;
+ case Urgency.CRITICAL:
+ stockIcon = 'gtk-dialog-error';
+ break;
+ }
+ return textureCache.load_icon_name(stockIcon, size);
+ }
+ },
+
Notify: function(appName, replacesId, icon, summary, body,
actions, hints, timeout) {
let source = this._sources[appName];
// been acknowledged.
if (source == null) {
let title = appNameMap[appName] || appName;
- source = new Source(title, icon, hints);
+ source = new Source(title);
Main.messageTray.add(source);
this._sources[appName] = source;
if (app)
source.setApp(app);
});
- } else {
- source.update(icon, hints);
}
summary = GLib.markup_escape_text(summary, -1);
notification = this._currentNotifications[id];
}
+ let iconActor = this._iconForNotificationData(icon, hints, source.ICON_SIZE);
if (notification == null) {
id = nextNotificationId++;
notification = new MessageTray.Notification(source, summary, body,
- { bannerBody: true });
+ { bannerBody: true,
+ icon: iconActor });
this._currentNotifications[id] = notification;
notification.connect('dismissed', Lang.bind(this,
function(n) {
}));
notification.connect('action-invoked', Lang.bind(this, this._actionInvoked, source, id));
} else {
- notification.update(summary, body, { clear: true });
+ notification.update(summary, body, { icon: iconActor,
+ clear: true });
}
if (actions.length) {
notification.setUrgent(hints.urgency == Urgency.CRITICAL);
- source.notify(notification);
+ let sourceIconActor = this._iconForNotificationData(icon, hints, source.ICON_SIZE);
+ source.notify(sourceIconActor, notification);
return id;
},
DBus.conformExport(NotificationDaemon.prototype, NotificationDaemonIface);
-function Source(title, icon, hints) {
- this._init(title, icon, hints);
+function Source(title) {
+ this._init(title);
}
Source.prototype = {
__proto__: MessageTray.Source.prototype,
- _init: function(title, icon, hints) {
+ _init: function(title) {
MessageTray.Source.prototype._init.call(this, title);
this.app = null;
this._openAppRequested = false;
-
- this.update(icon, hints);
- },
-
- update: function(icon, hints) {
- this._icon = icon;
- this._iconData = hints.icon_data;
- this._urgency = hints.urgency;
},
- createIcon: function(size) {
- let textureCache = St.TextureCache.get_default();
-
- if (this._icon) {
- if (this._icon.substr(0, 7) == 'file://')
- return textureCache.load_uri_async(this._icon, size, size);
- else if (this._icon[0] == '/') {
- let uri = GLib.filename_to_uri(this._icon, null);
- return textureCache.load_uri_async(uri, size, size);
- } else
- return textureCache.load_icon_name(this._icon, size);
- } else if (this._iconData) {
- let [width, height, rowStride, hasAlpha,
- bitsPerSample, nChannels, data] = this._iconData;
- return textureCache.load_from_raw(data, data.length, hasAlpha,
- width, height, rowStride, size);
- } else {
- let stockIcon;
- switch (this._urgency) {
- case Urgency.LOW:
- case Urgency.NORMAL:
- stockIcon = 'gtk-dialog-info';
- break;
- case Urgency.CRITICAL:
- stockIcon = 'gtk-dialog-error';
- break;
- }
- return textureCache.load_icon_name(stockIcon, size);
- }
+ notify: function(icon, notification) {
+ this._setSummaryIcon(icon);
+ MessageTray.Source.prototype.notify.call(this, notification);
},
clicked: function() {